class Operate {
    doOperate() {
        throw new Error("操作需要实现")
    }
}
/**
 * for循环的操作
 */
class ForOperate extends Operate {
    constructor(templateEngine, forOption) {
        super()
        this.templateEngine = templateEngine
        this.forOption = forOption
        this.process = []
        this.flag = true
    }
    addObj(obj) {
        //我也不知道为什么这样做就能解决顺序不是预期的问题
        if (this.flag) {
            this.process.unshift(obj)
        } else {
            this.process.push(obj)
        }
        this.flag = !(obj instanceof ForOperate)
    }
    doOperate() {
        let res = []
        let keyName = this.forOption.options.keyName
        let valueName = this.forOption.options.valueName
        let collectionName = this.forOption.collectionName
        let collection = this.forOption.options.collection
        this.changeForContent(collectionName, collection)
        for (let key in collection) {
            this.changeForContent(keyName, key)
            this.changeForContent(valueName, collection[key])
            this.process.forEach(p => {
                if (p instanceof Operate) {
                    res.push(...p.doOperate())
                } else {
                    this.parseLine(p, res)
                }
            })
        }

        return res
    }
    changeForContent(k, v) {
        this.templateEngine.doEvalSet(k, '', v)
    }
    parseLine(line, res) {
        this.templateEngine.parseLine(line, res)
    }
}
class TemplateEngine {
    constructor(arr, view) {
        this.arr = arr
        this.view = view
        //下标越小，作用域越小。content[i]是{}
        this.content = [view]
        //进程控制栈
        this.process = []
        //for循环的上下文
        this.forOperate = null
        this.forContent = []
        //模板中自定义标识符的命名规则
        this.varibleNameRegex = /[\w_]/g
        //${user}这种格式
        this.flagRegex = /\$\{(.+?)\}/g;
    }
    //将str中连续的空格转成一个空格，但不转单引号内的空格
    replaceMoreSpaceToOneSpace(str) {
        // 使用正则表达式进行替换
        return str.replace(/'[^']*'|\s+/g, function (match) {
            // 如果匹配到的是单引号内的内容，直接返回原文
            if (match[0] === "'") {
                return match;
            }
            // 否则，替换连续多个空格为一个空格
            return ' ';
        });
    }
    //获取for循环参数
    getForOptions(line) {
        //#for key,value in list
        //#for value in list
        line = this.replaceMoreSpaceToOneSpace(line)
        let options = line.split(' ')
        if (options.length != 4) {
            throw new Error("For的语法不对，For循环应该由4段组成，而不是" + options.length)
        }
        //解析for的key和value
        let i1 = options[1].split(',')
        let keyName, valueName, collectionName
        if (i1.length === 1) {
            keyName = "key"
            valueName = i1[0]
        } else if (i1.length === 2) {
            keyName = i1[0]
            valueName = i1[1]
        } else {
            throw new Error("For的语法不对，接受字段数量不应该为" + i1.length)
        }
        //遍历的对象名
        collectionName = options[3]
        //实际指向的对象
        let collection = this.getContentArgument(collectionName)
        return {
            keyName,
            valueName,
            collectionName,
            collection
        }
    }
    /**
     * //执行obj.user.name
     * @param {*} obj 总上下文
     * @param {*} expression 表达式 比如user.name
     * @returns 
     */
    doEval(obj, expression) {
        expression = expression.replace(this.flagRegex, (match, key) => {
            return "obj." + key
        })
        // console.log(expression)
        try {
            return eval("obj." + expression)
        } catch (e) {
            return undefined
        }
    }
    //获取上下文的参数，比如想执行user.name
    getContentArgument(argumentName) {
        // // console.log(JSON.stringify(this.content), argumentName)
        // console.log(this.content, argumentName)
        for (let content of this.content) {
            let val = this.doEval(content, argumentName)
            if (val !== undefined) {
                return val;
            }
        }
        throw new Error("无法执行表达式" + argumentName)
    }
    /**
     * 设置上下文参数
     * @param {String} varibleRootName 对象名
     * @param {String} varibleFieldName 对象的属性名
     * @param {Object} value 要设置的属性值
     */
    doEvalSet(varibleRootName, varibleFieldName, value) {
        if (varibleFieldName !== '') {
            varibleFieldName = "." + varibleFieldName
        }
        for (let i = 0, l = this.content.length; i < l; i++) {
            let currentContent = this.content[i]
            try {
                //通过eval设置，安全性暂未考虑
                eval(`currentContent.${varibleRootName}${varibleFieldName}=value`)
                break
            } catch (e) {

            }
        }
    }
    /**
     * 执行上下文的方法，比如执行home.user.sayHello()
     * @param {String} objectName 方法所处的对象名，比如home.user
     * @param {String} methodName 执行的方法名，比如sayHello
     * @param {Array} args 方法的参数
     * @returns 
     */
    doEvalCall(objectName, methodName, args) {
        for (let i = 0, l = this.content.length; i < l; i++) {
            let currentContent = this.content[i]
            try {
                if (methodName != '') {
                    //执行home.user.add(),objectName是home.user，this也是home.user
                    //如果方法名为空，则objectName是方法名，方法内部的this为user
                    return eval(`currentContent.${objectName}.${methodName}.apply(currentContent.${objectName},args)`)
                } else {
                    //如果方法名为空，则objectName是方法名，方法内部的this为currentContent
                    return eval(`currentContent.${objectName}.apply(currentContent,args)`)
                }
            } catch (e) {

            }
        }
    }
    //设置上下文的参数，比如设置home.user.name=value
    setVirable(varible, value) {
        varible = varible.split(".")
        let varibleRootName = varible[0]
        let varibleFieldName = varible.slice(1).join(".")
        this.doEvalSet(varibleRootName, varibleFieldName, value)

    }
    //执行for操作
    // doFor(res) {
    //     //这里要是最后第一个for的参数
    //     let forOption = this.process.filter(p => p.name === 'for').at(-1)
    //     let argument = forOption.argument
    //     //遍历迭代对象
    //     for (let i in forOption.options.collection) {
    //         //更新上下文变量
    //         argument[forOption.options.keyName] = i
    //         argument[forOption.options.valueName] = forOption.options.collection[i]
    //         //执行for语句内的行
    //         // console.log(JSON.stringify(this.forContent))
    //         this.forContent.forEach(forC => {
    //             forC.forEach(line => {
    //                 let parsedLine = this.parseFlag(line)
    //                 if (parsedLine === undefined) {
    //                     //如果parsedLine不存在，说明这行是指令行
    //                 } else {
    //                     res.push(parsedLine)
    //                 }
    //             })
    //         })
    //     }
    // }
    //处理set指令
    doSetOperate(line) {
        line = this.replaceMoreSpaceToOneSpace(line)
        //line=this.parseFlag(line)
        let options = line.split(" ")
        let varible = options[1]
        //模板变量名校验
        if (!varible.match(this.varibleNameRegex)) {
            throw new Error("非法标识符:" + varible)
        }
        //解析value值
        let value = this.parseFlag(options[2])
        //设置变量
        this.setVirable(varible, value)
        // console.log(this.content)
    }
    //处理call指令
    doCallOperate(line,lineIndex) {
        line = this.replaceMoreSpaceToOneSpace(line)
        let options = line.split(" ")
        let callMethod = options[1]
        //参数全放在一个数组中，用slice截取后面所有参数
        let args = options.slice(2)
        //参数全部解析
        args = args.map(arg => this.parseFlag(arg))
        callMethod = callMethod.split(".")
        let objectName = callMethod[0]
        let methodName = callMethod.slice(1).join(".")
        if(objectName==='log'){
            args=[lineIndex,...args]
        }
        //执行call指令
        this.doEvalCall(objectName, methodName, args)
    }
    //处理log指令
    doLogOperate(line, lineIndex) {
        line = line.substring(4)
        let str = line.replace(this.flagRegex, (match, key) => {
            let value = undefined
            try {
                value = this.getContentArgument(key)
            } catch (e) {
                value = "undefined"
            }
            return JSON.stringify(value)
        })
        this.log(lineIndex, str)
    }
    log(lineIndex, str) {
        console.log(`第${lineIndex + 1}行:`, str)
    }
    //解析片段
    parseFlag(line) {
        //如果line是直接${user}这种形式，则将其解析，并返回出去
        let matchArr = line.match(this.flagRegex)
        if (matchArr && matchArr.length === 1 && line.startsWith("${") && line.endsWith("}")) {
            line = line.substring(2).slice(0, -1)
            console.log(matchArr)
            let value = undefined
            try {
                value = this.getContentArgument(line)
            } catch (e) {
                throw new Error(`解析\n${line}\n出错，原因是\n${e.message}`)
            }
            // 如果找到了值，则返回，否则返回空字符串
            return value !== undefined ? value : '';
        }
        //如果line包含其他内容，那么就做字符串拼接的解析
        //会调用value.toString，所以数组会被解析成1,2,3,格式，没有中括号
        return line.replace(this.flagRegex, (match, key) => {
            let value = undefined
            try {
                value = this.getContentArgument(key)
            } catch (e) {
                // console.log(this.content)
                throw new Error(`解析行\n${line}\n出错，原因是\n${e.message}`)
            }
            // console.log(line, value)
            // 如果找到了值，则返回，否则返回空字符串
            return value !== undefined ? value : '';
        })
    }
    //解析一行，将解析结果push到res中，但指令行没结果，所以不push
    parseLine(line, res) {
        if (this.forContent[0]) {
            if (this.process[0].name === 'for' && this.process.length > 1 && this.process[1].name === 'end for') {

            } else if (this.process[0].name === 'end for' && this.process[1].name === 'for') {
                //用了我一下午的时间，终于把嵌套for给写出来了。代码很复杂，我也不知道逻辑怎么弄的了
                let forOption = this.process.filter(p => p.name === 'for').at(-1)
                if (this.forOperate == null) {
                    this.forOperate = new ForOperate(this, forOption)
                } else {
                    let innerForOperate = this.forOperate
                    this.forOperate = new ForOperate(this, forOption)
                    this.forOperate.addObj(innerForOperate)
                }
                let forOperate = this.forOperate
                let stackTop = this.forContent.pop()
                if (stackTop.name !== 'end for') {
                    throw new Error("不是预期的值")
                }
                // console.log(this.forContent.length)
                while (true) {
                    stackTop = this.forContent.pop()
                    if (stackTop === undefined) {
                        throw new Error("不是预期的结果，for没有开始标记")
                    }
                    if (stackTop.name === 'for') {
                        break
                    } else {
                        forOperate.addObj(stackTop)
                    }
                }
                //process中有end for和for，所以要shift两次
                this.process.shift()
                this.process.shift()
                //如果堆栈空了，说明可以执行for的内容了
                if (this.forContent.length === 0) {
                    res.push(...forOperate.doOperate())
                }
            } else if (this.process[0].name === 'for') {
                this.forContent.push(line)

            }
        } else {
            // console.log("无进程",line)
            res.push(this.parseFlag(line))
        }
    }
    render() {
        // console.log(this.arr)
        let res = []
        //逐行解析
        this.arr.forEach((line, lineIndex) => {
            if (line.trim().startsWith('$##')) {
                line = line.substring(1)
            } else if (line.trim().startsWith('##')) {
                return
            }
            let trimedLine = line.trim()
            if (trimedLine.startsWith('#for ')) {
                //解析for指令
                let options = this.getForOptions(trimedLine)
                let argument = {}
                let forOption = {
                    name: 'for',
                    options,
                    argument
                }
                this.forContent.push(forOption)

                //往上下文中添加循环参数
                this.content.unshift(argument)
                //设置迭代对象
                for (let i in options.collection) {
                    argument[options.keyName] = i
                    argument[options.valueName] = options.collection[i]
                    //只需要进行初始化，所以要break
                    break
                }
                this.process.push(forOption)
            } else if (trimedLine == '#end for') {
                //解析end for指令
                if (this.process[0] && this.process[0].name === 'for') {
                    // this.process.shift()
                    let forIndex = this.process[0].forIndex
                    this.forContent.push({
                        name: 'end for'
                    })
                    this.process.unshift({
                        name: 'end for'
                    })
                    this.parseLine(line, res)
                } else {
                    throw new Error("没有与#end for对应的符号")
                }
            } else if (trimedLine.startsWith('#set ')) {
                this.doSetOperate(trimedLine)
            } else if (trimedLine.startsWith('#call ')) {
                this.doCallOperate(trimedLine,lineIndex)
            }
            else {
                //不是特殊开头，则直接解析此行，不做指令解析
                this.parseLine(line, res)
            }
        })
        return res.join("\n")
    }
}

class StringTemplate {
    constructor(templateString) {
        this.arr = templateString.split('\n')
    }
    render(data) {
        return new TemplateEngine(this.arr, data).render()
    }
}
export default StringTemplate